<!--
 * @Date: 2022-03-27 15:32:23
 * @LastEditors: sailornpg
 * @LastEditTime: 2022-11-26 12:01:14
 * @FilePath: \vue-admin-ts\src\components\Form\MyForm.vue
 * @Description: something  
  
-->
<template>
  <el-form
    ref="form"
    class="form-wrapper"
    v-if="model"
    :validate-on-rule-change="false"
    :model="model"
    :rules="rules"
    v-bind="$attrs"
  >
    <el-row :gutter="gutter">
      <!-- :span="item.span || 24 / colCount" -->
      <el-col v-for="(item, index) in options" :key="index" v-bind="colLayout">
        <slot
          v-if="item.slot"
          :name="item.slot"
          :scope="returnScope(form!,model,rules,item)"
          v-bind="item.attrs"
        ></slot>
        <el-form-item
          v-else-if="!item.children || !item.children!.length"
          :prop="item.prop"
          :label="item.label"
          :style="itemStyle"
        >
          <component
            v-if="item.type !== 'upload' && item.type !== 'editor'"
            :placeholder="item.placeholder"
            v-bind="item.attrs"
            :is="`el-${item.type}`"
            v-model="model[item.prop!]"
          ></component>
          <el-upload
            v-if="item.type === 'upload'"
            v-bind="item.uploadAttrs"
            :on-preview="onPreview"
            :on-remove="onRemove"
            :on-success="onSuccess"
            :on-error="onError"
            :on-progress="onProgress"
            :on-change="onChange"
            :before-upload="beforeUpload"
            :before-remove="beforeRemove"
            :http-request="httpRequest"
            :on-exceed="onExceed"
          >
            <slot name="uploadArea"></slot>
            <template #tip>
              <slot name="uploadTip"></slot>
            </template>
          </el-upload>
          <div id="editor" v-if="item.type === 'editor'"></div>
        </el-form-item>
        <el-form-item
          v-if="item.children && item.children.length"
          :prop="item.prop"
          :label="item.label"
          :style="itemStyle"
        >
          <component
            :placeholder="item.placeholder"
            v-bind="item.attrs"
            :is="`el-${item.type}`"
            v-model="model[item.prop!]"
          >
            <component
              v-for="(child, i) in item.children"
              :key="i"
              :is="`el-${child.type}`"
              :label="child.label"
              :value="child.value"
            ></component>
          </component>
        </el-form-item>
      </el-col>
      <!-- :span="24 / colCount" -->
      <el-col v-bind="colLayout">
        <el-form-item>
          <slot name="action" :form="form" :model="model"></slot>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script lang="ts" setup>
import { FormOptions, FormInstance, ISlotScope } from "./type/type";
import { PropType, onMounted, ref, watch, nextTick } from "vue";
import cloneDeep from "lodash/cloneDeep";
import E from "wangeditor";
import {
  UploadFile,
  UploadFiles,
  UploadProgressEvent,
  UploadRawFile,
  UploadRequestOptions,
} from "element-plus";
let model = ref<any>(null);
let rules = ref<any>(null);
let form = ref<FormInstance | null>(null);
let wangEditor = ref();
const returnScope: (
  form: FormInstance,
  model: any,
  rules: any,
  option: FormOptions,
) => ISlotScope = (
  form: FormInstance,
  model: any,
  rules: any,
  option: FormOptions,
) => {
  return {
    form: form,
    model: model,
    rules: rules,
    option: option,
  };
};
const emits = defineEmits([
  "on-preview",
  "on-remove",
  "on-success",
  "on-error",
  "on-progress",
  "on-change",
  "before-upload",
  "before-remove",
  "on-exceed",
]);
const props = defineProps({
  //表单配置项
  options: {
    type: Array as PropType<FormOptions[]>,
    required: true,
  },
  //自定义上传方法
  httpRequest: {
    type: Object as PropType<UploadRequestOptions>,
  },
  colCount: {
    type: Number,
    default: 1,
  },
  colLayout: {
    type: Object,
    default: () => {
      return {
        xl: 6,
        lg: 8,
        md: 12,
        sm: 24,
        xs: 24,
      };
    },
  },
  gutter: {
    type: Number,
    default: 0,
  },
  itemStyle: {
    type: Object,
    default: () => ({}),
  },
});
//表单验证
const formValidate = () => {
  return form.value!.validate;
};
//获取表单数据
const getFormData = () => {
  return model.value;
};
const initFrom = () => {
  if (props.options && props.options.length) {
    let m: any = {};
    let r: any = {};
    props.options.map((item) => {
      m[item.prop!] = item.value;
      r[item.prop!] = item.rules;
      if (item.type === "editor") {
        //初始化富文本
        nextTick(() => {
          if (document.getElementById("editor")) {
            const editor = new E("#editor");
            //保存实例
            wangEditor.value = editor;
            editor.config.placeholder = item.placeholder!;
            editor.create();
            //富文本内容初始化，传入value
            editor.txt.html(item.value);
            //实时改变内容复制给model,提交时拿到最新的值
            editor.config.onchange = (val: string) => {
              model.value[item.prop!] = val;
            };
          }
        });
      }
    });
    model.value = cloneDeep(m);
    rules.value = cloneDeep(r);
  }
};
const resetFields = () => {
  // console.log(model.value);
  //重置element表单
  const clearFlag = Object.values(model.value).every((item) => item !== "");
  if (!clearFlag) {
    return;
  }
  form.value!.resetFields();
  if (props.options && props.options.length) {
    let edit = props.options.find((item) => item.type === "editor");
    if (edit) {
      wangEditor.value.txt.html(edit.value);
    }
  }
};
const onPreview = (uploadFile: UploadFile) => {
  emits("on-preview", uploadFile);
};
const onRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("on-remove", { uploadFile, uploadFiles });
};
const onSuccess = (
  response: any,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
) => {
  let uploadRes = props.options.find((item) => {
    return item.type === "upload";
  });
  model.value[uploadRes!.prop!] = { response, uploadFile, uploadFiles };
  emits("on-success", { response, uploadFile, uploadFiles });
};
const onError = (
  error: Error,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
) => {
  emits("on-error", { error, uploadFile, uploadFiles });
};
const onProgress = (
  evt: UploadProgressEvent,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
) => {
  emits("on-progress", { evt, uploadFile, uploadFiles });
};
const onChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("on-change", { uploadFile, uploadFiles });
};
const onExceed = (files: File[], uploadFiles: UploadFiles) => {
  emits("on-exceed", { files, uploadFiles });
};
const beforeUpload = (rawFile: UploadRawFile) => {
  emits("before-upload", rawFile);
};

const beforeRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("before-remove", { uploadFile, uploadFiles });
};
onMounted(() => {
  initFrom();
});
watch(
  () => props.options,
  () => {
    initFrom();
  },
  { deep: true },
);
//派发方法或者属性
defineExpose({
  resetFields,
  formValidate,
  getFormData,
});
</script>
<style lang="less" scoped>
.form-wrapper {
  padding-top: 22px;
  .el-row {
    flex-wrap: wrap;
  }
}
</style>
